﻿using System;
using System.Collections.Generic;
using System.Reflection;
using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MugenInjection;
using MugenInjection.Bindings;
using MugenInjection.Infrastructure;
using MugenInjection.Interface;
using MugenInjection.Parameters;
using MugenInjection.Scope;
using TestModels.Class;
using Should;

namespace MugenInjectionTest.Activators
{
    public abstract class ActivatorBaseTest : TestBase
    {
        protected abstract IActivator GetActivator();

        [TestMethod]
        public virtual void ActivateWithParametersAndThenWithoutTest()
        {
            IActivator activator = GetActivator();
            const string constructorParam = "constructorParam";
            const string propertyParam = "PropertyTest";
            const string methodParam = "methodParam";

            ConstructorInfo constructorInfo = typeof(ActivatorTestModel).GetConstructor(new[] { typeof(string) });
            var parameters = new List<IInjectionParameter>
                {
                    new ConstructorParameter("constructorTest", constructorParam),
                    new PropertyParameter("PropertyTest", propertyParam),
                    new MethodParameter("MethodTest", "test", methodParam)
                };

            var injector = GetInjector();
            var activate =
                activator.Activate(constructorInfo,
                                   new BindingContext(typeof(ActivatorTestModel), null, null, injector,
                                                      injector, parameters.ToArray(), new Dictionary<string, object>()))
                as ActivatorTestModel;
            activate.ShouldNotBeNull("Object not created");
            activate.Constructor.ShouldEqual(constructorParam, "Constructor parameter not equal");
            activate.Property.ShouldEqual(propertyParam, "Property parameter not equal");
            activate.Method.ShouldEqual(methodParam, "Method parameter not equal");

            const string injectParameter = "Test";
            injector.AddBinding(new ConstantBinding(injectParameter, new[] { typeof(string) }, null, context => true));

            //Clear parameters
            parameters.Clear();
            activate =
                activator.Activate(constructorInfo,
                                   new BindingContext(typeof(ActivatorTestModel), null, null,
                                                      injector, injector, parameters.ToArray(),
                                                      new Dictionary<string, object>())) as ActivatorTestModel;

            activate.ShouldNotBeNull("Object not created");
            activate.Constructor.ShouldEqual(injectParameter, "Constructor parameter not equal");
            activate.Property.ShouldEqual(injectParameter, "Property parameter not equal");
            activate.Method.ShouldEqual(injectParameter, "Method parameter not equal");
        }

        [TestMethod]
        public virtual void ActivateWithNotAttributeTypeTest()
        {
            IActivator activator = GetActivator();

            const string constructorParam = "constructorParam";
            const string propertyParam = "PropertyTest";
            const string methodParam = "methodParam";

            ConstructorInfo constructorInfo = typeof(ActivatorTestModel).GetConstructor(new[] { typeof(string) });
            var parameters = new List<IInjectionParameter>
                {
                    new ConstructorParameter("constructorTest", constructorParam),
                    new PropertyParameter("PropertyTest", propertyParam),
                    new MethodParameter("MethodTest", "test", methodParam)
                };

            var injector = GetInjector();
            injector.Settings.AttributeForInject = typeof(TestMethodAttribute);

            var activate =
                activator.Activate(constructorInfo,
                                   new BindingContext(typeof(ActivatorTestModel), null, null, injector,
                                                      injector, parameters.ToArray(), new Dictionary<string, object>()))
                as ActivatorTestModel;

            activate.ShouldNotBeNull("Object not created");
            activate.Constructor.ShouldEqual(constructorParam, "Constructor parameter not equal");
            activate.Property.ShouldBeNull();
            activate.Method.ShouldBeNull();
        }

        [TestMethod]
        public virtual void InjectWithParametersAndThenWithoutTest()
        {
            IActivator activator = GetActivator();

            const string propertyParam = "PropertyTest";
            const string methodParam = "methodParam";

            var parameters = new List<IInjectionParameter>
                {
                    new PropertyParameter("PropertyTest", propertyParam),
                    new MethodParameter("MethodTest", "test", methodParam)
                };

            var injector = GetInjector();

            var activateModel = new ActivatorTestModel();
            activator.Activate(ref activateModel,
                               new BindingContext(typeof(ActivatorTestModel), null, null, injector,
                                                  injector, parameters.ToArray(), new Dictionary<string, object>()));

            activateModel.Property.ShouldEqual(propertyParam, "Property parameter not equal");
            activateModel.Method.ShouldEqual(methodParam, "Method parameter not equal");

            const string injectParameter = "Test";
            injector.AddBinding(new ConstantBinding(injectParameter, new[] { typeof(string) },
                                                    null, context => true));

            //Clear parameter
            parameters.Clear();

            activator.Activate(ref activateModel,
                               new BindingContext(typeof(ActivatorTestModel), null, null, injector,
                                                  injector, parameters.ToArray(), new Dictionary<string, object>()));

            activateModel.Property.ShouldEqual(injectParameter, "Property parameter not equal");
            activateModel.Method.ShouldEqual(injectParameter, "Method parameter not equal");
        }

        [TestMethod]
        public virtual void InjectWithNotAttributeTypeTest()
        {
            IActivator activator = GetActivator();

            const string propertyParam = "PropertyTest";
            const string methodParam = "methodParam";


            var parameters = new List<IInjectionParameter>
                {
                    new PropertyParameter("PropertyTest", propertyParam),
                    new MethodParameter("MethodTest", "test", methodParam)
                };

            var injector = GetInjector();
            injector.Settings.AttributeForInject = typeof(TestMethodAttribute);

            var activateModel = new ActivatorTestModel();
            activator.Activate(ref activateModel,
                               new BindingContext(typeof(ActivatorTestModel), null, null, injector,
                                                  injector, parameters.ToArray(), new Dictionary<string, object>()));

            activateModel.Property.ShouldBeNull();
            activateModel.Method.ShouldBeNull();
        }

        [TestMethod]
        public virtual void ActivateStructWithParametersAndThenWithoutTest()
        {
            Type modelType = typeof(ActivatorTestModelStruct);
            IActivator activator = GetActivator();
            const string constructorParam = "constructorParam";
            const string propertyParam = "PropertyTest";
            const string methodParam = "methodParam";

            ConstructorInfo constructorInfo = modelType.GetConstructor(new[] { typeof(string) });
            var parameters = new List<IInjectionParameter>
                {
                    new ConstructorParameter("constructorTest", constructorParam),
                    new PropertyParameter("PropertyTest", propertyParam),
                    new MethodParameter("MethodTest", "test", methodParam)
                };

            var injector = GetInjector();
            var activate = (ActivatorTestModelStruct)
                           activator.Activate(constructorInfo,
                                              new BindingContext(modelType, null, null, injector,
                                                                 injector, parameters.ToArray(),
                                                                 new Dictionary<string, object>()));


            activate.Constructor.ShouldEqual(constructorParam, "Constructor parameter not equal");
            activate.Property.ShouldEqual(propertyParam, "Property parameter not equal");
            activate.Method.ShouldEqual(methodParam, "Method parameter not equal");

            const string injectParameter = "Test";
            injector.AddBinding(new ConstantBinding(injectParameter, new[] { typeof(string) },
                                                    null, context => true));

            //Clear parameter
            parameters.Clear();
            activate =
                (ActivatorTestModelStruct)activator.Activate(constructorInfo,
                                                              new BindingContext(modelType, null, null,
                                                                                 injector, injector,
                                                                                 parameters.ToArray(),
                                                                                 new Dictionary<string, object>()));

            activate.Constructor.ShouldEqual(injectParameter, "Constructor parameter not equal");
            activate.Property.ShouldEqual(injectParameter, "Property parameter not equal");
            activate.Method.ShouldEqual(injectParameter, "Method parameter not equal");
        }

        [TestMethod]
        public virtual void ActivateStructWithNotAttributeTypeTest()
        {
            Type modelType = typeof(ActivatorTestModelStruct);

            IActivator activator = GetActivator();
            const string constructorParam = "constructorParam";
            const string propertyParam = "PropertyTest";
            const string methodParam = "methodParam";

            ConstructorInfo constructorInfo = modelType.GetConstructor(new[] { typeof(string) });
            var parameters = new List<IInjectionParameter>
                {
                    new ConstructorParameter("constructorTest", constructorParam),
                    new PropertyParameter("PropertyTest", propertyParam),
                    new MethodParameter("MethodTest", "test", methodParam)
                };

            var injector = GetInjector();
            injector.Settings.AttributeForInject = typeof(TestMethodAttribute);

            var activate = (ActivatorTestModelStruct)
                           activator.Activate(constructorInfo,
                                              new BindingContext(modelType, null, null, injector,
                                                                 injector, parameters.ToArray(),
                                                                 new Dictionary<string, object>()));

            activate.Constructor.ShouldEqual(constructorParam, "Constructor parameter not equal");
            activate.Property.ShouldBeNull();
            activate.Method.ShouldBeNull();            
        }

        [TestMethod]
        public virtual void InjectStructWithParametersAndThenWithoutTest()
        {
            Type modelType = typeof(ActivatorTestModelStruct);
            IActivator activator = GetActivator();

            const string propertyParam = "PropertyTest";
            const string methodParam = "methodParam";

            var parameters = new List<IInjectionParameter>
                {
                    new PropertyParameter("PropertyTest", propertyParam),
                    new MethodParameter("MethodTest", "test", methodParam)
                };

            var injector = GetInjector();

            object objActivateModel = new ActivatorTestModelStruct();
            activator.Activate(ref objActivateModel, new BindingContext(modelType, null, null, injector,
                                                                             injector, parameters.ToArray(),
                                                                             new Dictionary<string, object>()));
            //unbox
            var activateModel = (ActivatorTestModelStruct)objActivateModel;
            activateModel.Property.ShouldEqual(propertyParam, "Property parameter not equal");
            activateModel.Method.ShouldEqual(methodParam, "Method parameter not equal");
            
            const string injectParameter = "Test";
            injector.AddBinding(new ConstantBinding(injectParameter, new[] { typeof(string) },
                                                    null, context => true));

            //Clear parameter
            parameters.Clear();

            activator.Activate(ref objActivateModel, new BindingContext(modelType, null, null, injector,
                                                                             injector, parameters.ToArray(),
                                                                             new Dictionary<string, object>()));

            //unbox
            activateModel = (ActivatorTestModelStruct)objActivateModel;
            activateModel.Property.ShouldEqual(injectParameter, "Property parameter not equal");
            activateModel.Method.ShouldEqual(injectParameter, "Method parameter not equal");            
        }

        [TestMethod]
        public virtual void InjectStructWithNotAttributeTypeTest()
        {
            IActivator activator = GetActivator();

            const string propertyParam = "PropertyTest";
            const string methodParam = "methodParam";


            var parameters = new List<IInjectionParameter>
                {
                    new PropertyParameter("PropertyTest", propertyParam),
                    new MethodParameter("MethodTest", "test", methodParam)
                };

            var injector = GetInjector();
            injector.Settings.AttributeForInject = typeof(TestMethodAttribute);

            object objActivateModel = new ActivatorTestModelStruct();
            activator.Activate(ref objActivateModel,
                               new BindingContext(typeof(ActivatorTestModelStruct), null, null, injector,
                                                  injector, parameters.ToArray(), new Dictionary<string, object>()));
            var activateModel = (ActivatorTestModelStruct)objActivateModel;
            activateModel.Property.ShouldBeNull();
            activateModel.Method.ShouldBeNull();            
        }
    }
}